<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 引入vue的cdn -->
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.3.4/vue.global.js"></script>
    <!-- 引入axios的cdn -->
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.5.0/axios.js"></script>

    <title>五子棋游戏</title>

    <style>
      * {
        padding: 0%;
        margin: 0;
        font-weight: 900;
        font-family: '幼圆';
        word-spacing: 2px;
        letter-spacing: 2px;
      }

      #app {
        min-width: 600px;
        max-width: 100vw;
        max-height: 100vh;
        position: relative;
      }

      .bg {
        background-image: url('bg/bg.jpg');
        background-repeat: no-repeat;
        background-position: center center;
        background-size: cover;
        position: absolute;
        z-index: -1;
      }

      .bg,
      .main {
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 100%;
      }

      .main {
        position: relative;
        min-height: 100vh;
      }

      .game-menu,
      .board-wrapper,
      .pvp-list-wrapper,
      .game-info-wrapper {
        background-color: rgba(255, 255, 255, 0.9);
        box-shadow: 3px 3px 10px 2px rgba(252, 216, 220, 0.4);
        position: absolute;
        padding: 20px;
        border-radius: 10px;
      }

      .board-wrapper {
        display: flex;
        justify-content: center;
        align-items: center;
        position: relative;
      }

      .board-fun-wrapper {
        position: absolute;
        width: 700px;
        height: 40px;
        top: -60px;
        background-color: #fff;
        display: flex;
        padding: 5px 20px;
        justify-content: space-around;
        align-items: center;
        font-size: 15px;
        border-radius: 10px;
      }

      .board-fun-item {
        padding: 5px 10px;
        display: flex;
        justify-content: flex-start;
        align-items: center;
        position: relative;
      }

      .board-fun-item:first-child {
        font-family: '黑体';
      }

      .board-fun-item:nth-child(2) {
        margin: 0 10px;
        flex: 2;
        height: 20px;
        overflow: hidden;
      }
      .board-fun-item-tips {
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .board-fun-item:nth-child(3) {
        margin-right: 10px;
      }
      .board-fun-item:nth-child(4) {
        margin-right: 10px;
      }

      .board-fun-tips-flag {
        width: 20px;
        height: 20px;
        border-radius: 50%;
        display: inline-block;
      }
      /* 棋盘颜色 */
      .board-2,
      .board-item-2 {
        border-color: #bccae7;
      }

      .board,
      .board-2 {
        border-width: 4px;
        border-style: solid;
        display: flex;
        flex-flow: wrap;
      }

      .board {
        position: absolute;
        z-index: 0;
      }

      .board-item,
      .board-item-2 {
        box-sizing: border-box;
        border-width: 1px;
        border-style: solid;
        cursor: pointer;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .board,
      .board-item {
        border-color: transparent;
      }

      .chess-back-ffa5d9 {
        background-color: #ffa5d9;
      }

      .chess-back-bccae7 {
        background-color: #bccae7;
      }

      .board-item:hover .chess-back {
        background: #ffe5ce;
      }

      .chess-back {
        border-radius: 50%;
      }

      .game-menu,
      .pvp-list-wrapper,
      .game-info-wrapper {
        width: 500px;
        height: 500px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        font-family: '幼圆';
        font-weight: 900;
      }

      .title,
      .game-info-title {
        margin-bottom: 40px;
        font-size: 40px;
        color: #c2c9e6;
        text-align: center;
      }

      .game-info {
        width: 100%;
        height: 80%;
        text-indent: 40px;
        line-height: 30px;
        font-size: 20px;
        font-weight: normal;
      }

      .btn,
      .btn2,
      .cancel-btn,
      .error-btn {
        display: flex;
        justify-content: center;
        align-items: center;
        border-radius: 25px;
        color: #fff;
        cursor: pointer;
      }

      .back-btn {
        width: 30px;
        height: 30px;
        display: flex;
        justify-content: center;
        align-items: center;
        border-radius: 50%;
        color: #fff;
        cursor: pointer;
        position: absolute;
        right: 5px;
        top: 5px;
      }

      .back-btn:hover {
        background-color: #24ebc4;
      }

      .btn {
        background: linear-gradient(90deg, #bccae7, #f9b7db, #ffe5ce);
      }

      .btn2 {
        background: linear-gradient(90deg, #24ebc4, #46abee, #cf8ee9);
      }

      .cancel-btn {
        background-color: #a0a2a2;
      }

      .error-btn {
        background-color: #f56c6c;
      }

      .unable-btn {
        background-color: #a1a2a3;
        border-radius: 25px;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #fff;
        padding: 5px 10px;
        font-size: 13px;
      }

      .menu-item {
        font-size: 20px;
        margin-bottom: 20px;
        width: 300px;
        height: 50px;
      }

      .btn:hover,
      .btn2:hover {
        animation: changeColor 1.5s linear infinite;
      }

      @keyframes changeColor {
        100% {
          filter: hue-rotate(360deg);
        }
      }

      .pvp-list-wrapper {
        position: absolute;
        left: 0;
        top: 0;
        background-color: #fff;
      }

      .pvp-list {
        width: 100%;
        height: 100%;
        overflow-y: auto;
      }

      *::-webkit-scrollbar {
        width: 0px;
      }

      .pvp-item-title-wrapper {
        width: 100%;
      }

      .pvp-item-title-wrapper,
      .pvp-item {
        display: flex;
        justify-content: space-around;
      }

      .pvp-item {
        cursor: pointer;
      }

      .pvp-item-name,
      .pvp-item-size {
        text-align: center;
        flex: 0.75;
      }

      .pvp-item-name,
      .pvp-item-p,
      .add-btn-wrapper,
      .pvp-item-size {
        padding: 10px 5px;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .pvp-item:nth-child(odd) {
        color: #c2c9e6;
      }

      .pvp-item:nth-child(even) {
        background-color: #c2c9e6;
        color: #fff;
      }

      .pvp-item-p {
        flex: 1;
        display: flex;
        justify-content: space-around;
      }

      .pvp-item-p-1,
      .pvp-item-p-2 {
        flex: 1;
      }

      .add-btn-wrapper {
        flex: 1;
        width: 200px;
        display: flex;
        justify-content: center;
        align-items: center;
      }

      .add-btn {
        padding: 5px 10px;
        font-size: 13px;
      }

      .pvp-item:hover {
        background-color: rgba(0, 0, 0, 0.1);
        color: #fff;
      }

      .create-btn {
        margin: 0 auto;
        margin-top: 20px;
        width: 150px;
        height: 40px;
        text-align: center;
      }

      .tip-wrapper {
        position: fixed;
        width: 300px;
        height: 150px;

        left: calc(50% - 150px);
        top: calc(50% - 100px);

        padding: 20px;
        background-color: #fff;
        border-radius: 10px;
        box-shadow: #a0a2a2 0 0 5px 1px;

        color: #8a9ce5;
        display: flex;
        justify-content: space-between;
        flex-direction: column;
        align-items: center;
      }

      .tip-title {
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 20px;
      }

      .tip-main {
        flex: 4;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
      }

      .tip-main > div {
        width: 100%;
      }

      .tip-ctrl {
        width: 100%;
        flex: 1;
        display: flex;
        justify-content: flex-end;
        align-items: center;
      }

      .input {
        outline: none;
        border: 1px #8e9fe6 solid;
      }

      .size-input {
        width: 50px;
      }

      .tip-cancel-btn,
      .tip-comfirm-btn {
        margin-right: 10px;
        padding: 5px 15px;
        font-size: 13px;
      }

      .float-tips {
        position: fixed;
        min-width: 300px;
        padding: 3px;
        border-radius: 5px;
        height: 30px;
        top: 7%;
        left: calc(50% - 150px);
        z-index: 9999;
        color: #fff;
        background-color: #fff;
        box-shadow: 1px 1px 5px 1px rgba(0, 0, 0, 0.1);
      }

      .float-tips-main {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        border-radius: 5px;
      }

      .float-tips-hidden {
        top: -7%;
        opacity: 0;
        transition: 1s ease-out;
      }

      .game-status-tips {
        position: fixed;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        z-index: 99999;
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 40px;
      }

      .game-status-item {
        font-family: 'MV Boli';
        font-weight: 900;
      }

      .game-status-item-hidden {
        transition: 1s ease-out;
        transform: scale(5);
        opacity: 0;
      }

      .wait-player {
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
        background-color: #fff;
        width: 300px;
        height: 100px;
        border-radius: 10px;
        padding: 10px;
        box-shadow: 1px 1px 5px 2px rgba(0, 0, 0, 0.1);
      }

      .wait-player-tips {
        flex: 3;
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 20px;
        color: #94a5e6;
      }

      .wait-player-tips span {
        opacity: 0;
        animation: loading 1s linear infinite;
      }
      @keyframes loading {
        100% {
          opacity: 1;
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="bg"></div>
      <div class="main" ref="main">
        <div class="board-wrapper" :style="board_show?'left:auto':'left:-200%'">
          <!-- 功能区 -->
          <div class="board-fun-wrapper">
            <div class="board-fun-item">{{gameTime}}</div>
            <div class="board-fun-item">
              <div class="board-fun-item-tips">
                {{change_game_tips.msg}}
                <div class="board-fun-tips-flag" :style="'background:'+change_game_tips.chess_color"></div>
              </div>
            </div>
            <div class="board-fun-item btn2" @click="back_tips_show=true">悔棋</div>
            <div class="board-fun-item error-btn" @click="handle_reconnect">重新连接</div>
            <div class="board-fun-item cancel-btn" @click="handle_exitChess">退出对战</div>
          </div>

          <!-- 棋盘 -->
          <div class="board-2" :style="{'width':chess_size*(board_col+1)+'px','height':chess_size*(board_row+1)+'px'}">
            <div
              class="board-item-2"
              :style="{'width':chess_size+'px','height':chess_size+'px'}"
              v-for="item in (board_col+1)*(board_row+1)"
            ></div>
          </div>

          <!-- 根据用户输入行列进行生成棋盘 -->
          <!-- 伪棋盘 -->
          <div class="board" :style="{'width':chess_size*board_col+'px','height':chess_size*board_row+'px'}">
            <div
              class="board-item"
              :style="{'width':chess_size+'px','height':chess_size+'px'}"
              v-for="item in board_col*board_row"
              @click="handle_setChess(item-1)"
            >
              <div
                :class="'chess-back '+getChessColor(item-1)"
                :style="{'width':chess_size/1.4+'px','height':chess_size/1.4+'px' }"
              ></div>
            </div>
          </div>
        </div>

        <!-- 游戏菜单界面 -->
        <div class="game-menu" :style="!board_show?'left:auto':'left:-200%'">
          <div class="menu-wrapper">
            <div class="title">五子棋游戏</div>
            <div class="menu-item btn" @click="handle_PvP_showBoard">双人对决</div>
            <div class="menu-item btn" @click="game_info_index=0;game_info_show=true">游戏说明</div>
            <div class="menu-item btn" @click="game_info_index=1;game_info_show=true">关于游戏</div>
            <div class="menu-item btn">退出游戏</div>
          </div>

          <!-- 双人对决棋局列表 -->
          <div class="pvp-list-wrapper" v-if="pvp_list_show">
            <div class="back-btn" @click="pvp_list_show=false">✖️</div>
            <div class="pvp-item-title-wrapper">
              <div class="pvp-item-name">名称</div>
              <div class="pvp-item-size">棋盘大小</div>
              <div class="add-btn-wrapper">棋局状态</div>
            </div>
            <div class="pvp-list">
              <div class="pvp-item" v-for="item,index in pvp_list" :key="item.chess_id">
                <div class="pvp-item-name">棋局{{index+1}}</div>
                <div class="pvp-item-size">{{item.size}}</div>
                <div class="add-btn-wrapper">
                  <div class="my-chess" v-if="item.p1_id==client_id">本人棋局</div>
                  <div
                    class="add-btn btn2"
                    v-else-if="(!item.p2_id)||(!item.p1_id)"
                    @click="handle_addChess(item.chess_id)"
                  >
                    加入棋局
                  </div>
                  <div class="unable-btn" v-else>棋局已满</div>
                </div>
              </div>
            </div>
            <div class="ctrl-wrapper">
              <div class="create-btn btn" @click="create_tips_show=true">创建棋局</div>
            </div>
          </div>

          <!-- 游戏说明等内容显示 -->
          <div class="game-info-wrapper" v-if="game_info_show">
            <div class="back-btn" @click="game_info_show=false">✖️</div>
            <div class="game-info-title">{{GameInfo[game_info_index].title}}</div>
            <div class="game-info">{{GameInfo[game_info_index].content}}</div>
          </div>
        </div>
      </div>

      <!-- 创建棋局提示框 -->
      <div class="tip-wrapper" v-if="create_tips_show">
        <div class="tip-title">提示</div>
        <div class="tip-main">
          <div>
            请输入棋盘大小：
            <input type="number" class="input size-input" min="5" max="15" v-model="model_size" />
            <span class="attention-text">(5-15)</span>
          </div>
          <div>注意：对战过程中请保持网络流畅，若2人均掉线，棋局会自动销毁。</div>
        </div>
        <div class="tip-ctrl">
          <div class="tip-cancel-btn cancel-btn" @click="create_tips_show=false">取消</div>
          <div class="tip-comfirm-btn btn2" @click="handle_createChess">确定</div>
        </div>
      </div>

      <!-- 加入棋局提示框 -->
      <div class="tip-wrapper" v-if="add_tips_show">
        <div class="tip-title">提示</div>
        <div class="tip-main">确定加入该棋局？注意：对战过程中请保持网络流畅，若2人均掉线，棋局会自动销毁。</div>
        <div class="tip-ctrl">
          <div class="tip-cancel-btn cancel-btn" @click="add_tips_show=false">取消</div>
          <div class="tip-comfirm-btn btn2" @click="handle_addChess_confirm">确定</div>
        </div>
      </div>

      <!-- 悔棋 -->
      <div class="tip-wrapper" v-if="back_tips_show">
        <div class="tip-title">提示</div>
        <div class="tip-main">您确定悔棋吗？如果对方已下棋，则不能悔棋。</div>
        <div class="tip-ctrl">
          <div class="tip-cancel-btn cancel-btn" @click="back_tips_show=false">取消</div>
          <div class="tip-comfirm-btn btn2" @click="handle_backChess_confirm">确定</div>
        </div>
      </div>

      <!-- 浮动提示框，会自动消失 -->
      <div class="float-tips" v-if="show_float_tips" :class="hidden_float_tips?'float-tips-hidden':''">
        <div class="float-tips-main" :style="{'background':bgColor}">{{msg}}</div>
      </div>

      <!-- 中央游戏提示文字 -->
      <div class="game-status-tips" v-if="show_game_status_tips">
        <div
          class="game-status-item"
          :style="{'color':game_status_tips_color}"
          :class="hidden_game_status_tips?'game-status-item-hidden':''"
        >
          {{game_status_tips}}
        </div>
      </div>

      <!-- 等待玩家加入 -->
      <div class="game-status-tips" v-if="is_wait_player">
        <div class="wait-player">
          <div class="wait-player-tips">
            等待玩家加入
            <span>.</span>
            <span>.</span>
            <span>.</span>
          </div>

          <div class="tip-ctrl">
            <div class="tip-cancel-btn cancel-btn" @click="handle_exitChess">退出棋局</div>
          </div>
        </div>
      </div>
    </div>

    <script>
      const { createApp, ref } = Vue
      createApp({
        setup() {
          // 常量
          const True = 'true'
          const False = 'false'
          const InfoColor = '#909399'
          const ErrorColor = '#F56C6C'
          const SuccessColor = '#67C23A'
          const BlueColor = '#409EFF'
          const ServerIp = '127.0.0.1:8880'

          const GameInfo = [
            {
              title: '游戏说明',
              content:
                '该游戏为双人对战游戏,要求设备连接网络,且需要双方保持良好的网络环境.悔棋机制说明:如果您需要悔棋时' +
                ',请在对方下棋前进行悔棋,否则无效,或与对方交流后,让对方先悔棋,然后到你悔棋则有效.' +
                '需要注意的是,对战时死局需要手动退出游戏,该对局不会自动销毁.感谢您的使用,祝您游戏愉快.'
            },
            {
              title: '关于游戏',
              content:
                '本游戏是受与课堂五子棋python课所启发,前端界面使用vue.js,后端使用python的tornado,代码开源,可用于学习.gitee地址:https://gitee.com/superlwb/python-gobang-game'
            }
          ]

          // 存储游戏说明与关于游戏菜单选项index
          const game_info_index = ref(0)

          // 存储该客户端id，相当于一个用户
          const client_id = new Date().getTime()
          console.log('client_id', client_id)

          // 棋子方格大小，单位px
          const chess_size = 42

          // 棋盘大小
          const board_col = ref(15)
          const board_row = ref(15)

          // 显示棋盘
          const board_show = ref(false)

          // 显示tips变量
          const create_tips_show = ref(false)
          const add_tips_show = ref(false)
          const back_tips_show = ref(false)
          const game_info_show = ref(false)

          // 显示双人对决棋局列表
          const pvp_list_show = ref(false)
          const pvp_list = ref([])

          // 显示游戏等待玩家加入
          const is_wait_player = ref(false)

          // 绑定input值
          const model_size = ref(15)

          // 绑定当前选中的棋局的id
          const curr_chess_id = ref('')

          // 绑定当前棋局棋盘信息
          const curr_chess_board = ref([])

          // 控制游戏结束后的提示
          const game_end_flag = ref(false)
          const win_id = ref('')
          const win_color = ref('')

          // 对方id
          const curr_p_id = ref('')

          // webSocket
          const is_connection = ref(false)
          var ws = new WebSocket(`ws://${ServerIp}?id=${client_id}&chess_id=${curr_chess_id.value}`)

          // 连接成功
          ws.onopen = function () {
            console.log('连接成功')
            is_connection.value = true
            showFloatTips('连接服务器成功', SuccessColor)
          }

          ws.onerror = function () {
            showFloatTips('连接服务器失败，请确认服务器已开启', ErrorColor)
          }

          // ws读取信息
          ws.onmessage = function (event) {
            // 将json字符串转成json
            // console.log(event.data)
            let res = JSON.parse(event.data)
            let msg = JSON.parse(res.message)
            // console.log(res)
            switch (res.type) {
              case 'id':
                // 连接后可以做什么...
                break
              case 'updatePvpList':
                let temp_list = []
                // 转为数组
                console.log('刷新pvp列表')
                for (const key in msg) {
                  if (Object.hasOwnProperty.call(msg, key)) {
                    const element = msg[key]
                    temp_list.push(JSON.parse(element))
                  }
                }

                pvp_list.value = temp_list
                break

              case 'waitPlayer':
                is_wait_player.value = true
                board_show.value = false
                clearInterval(gameTimer.value)
                break
              case 'playgame':
                console.log('加入成功')
                // 先清除上一次的记录
                curr_chess_board.value = []
                // 棋局信息
                curr_chess_board.value = JSON.parse(msg.board)
                // 棋局大小
                board_col.value = board_row.value = parseInt(msg.size)

                curr_p_id.value = res.send_id
                pvp_list_show.value = false
                board_show.value = true
                is_wait_player.value = false
                showFloatTips('加入棋局成功', SuccessColor)
                showGameStatusTips('Game Begin', BlueColor)
                setTimer()
                changeGameTips('游戏开始，祝您游戏愉快')
                break
              case 'setChessPos':
                console.log('放置棋子成功')
                // 棋局信息
                // console.log(JSON.parse(msg.board))
                curr_chess_board.value = JSON.parse(JSON.parse(msg.board).board)

                changeGameTips('棋子放置' + msg.pos + '成功，放置方为：', msg.set_color)

                break
              case 'backChess':
                console.log('悔棋了')
                curr_chess_board.value = JSON.parse(JSON.parse(msg.chess_board).board)
                changeGameTips('悔棋位置' + msg.back_pos + '，悔棋方为：', msg.back_color)
                break

              case 'gameEnd':
                win_id.value = msg.win_id
                win_color.value = msg.win_color
                game_end_flag.value = true
                console.log('游戏结束')
                // showFloatTips('加入棋局成功', SuccessColor)
                showGameStatusTips('Game Over', ErrorColor)
                changeGameTips('游戏结束，获胜方为：', msg.win_color)
                clearInterval(gameTimer.value)
                break
            }
          }

          ws.onclose = function (e) {
            is_connection.value = false
            console.log('connection closed (' + e.code + ')')
            showFloatTips('已断开与服务器连接', InfoColor)
          }

          const handle_reconnect = () => {
            if (is_connection.value) {
              showFloatTips('服务器连接正常', InfoColor)
              return
            }
            ws = new WebSocket(`ws://${ServerIp}?id=${client_id}&chess_id=${curr_chess_id.value}`)
          }

          // 显示棋盘
          const showBoard = () => {
            // 显示棋盘
            board_show.value = true
          }

          // 人人对决模式
          const handle_PvP_showBoard = async () => {
            await getPvpList()
            pvp_list_show.value = true
          }

          // 设置棋子
          const handle_setChess = async (index) => {
            // 列
            const col = index % board_col.value
            // 行
            const row = parseInt(index / board_col.value)
            // console.log(row, col)
            if (curr_chess_id.value == '') {
              console.log('必要参数不能为空')
              showFloatTips('必要参数不能为空', ErrorColor)
              return
            }
            // 发送坐标
            const { data: res } = await axios.post(
              `http://${ServerIp}/sendPos?chess_id=${curr_chess_id.value}&col=${col}&row=${row}&send_p_id=${client_id}`
            )
            if (res.error === True) {
              console.log(res.msg)
              showFloatTips(res.msg, ErrorColor)
              return
            }
            showFloatTips('下棋成功', SuccessColor)
          }

          // 获取pvp列表
          const getPvpList = async () => {
            const { data: res } = await axios.get(`http://${ServerIp}/pvp`)
            let temp_list = []
            // 转为数组
            for (const key in res) {
              if (Object.hasOwnProperty.call(res, key)) {
                const element = res[key]
                temp_list.push(JSON.parse(element))
              }
            }
            pvp_list.value = temp_list
          }
          getPvpList()
          // 创建棋局
          const handle_createChess = async () => {
            if (model_size.value < 5 || model_size.value > 15) {
              model_size.value = 15
              return
            }
            const { data: res } = await axios.post(
              `http://${ServerIp}/pvp?p_id=${client_id}&size=${model_size.value}&create=true`
            )
            if (res.error === True) {
              console.log(res.msg)
              showFloatTips(res.msg, ErrorColor)
              return
            }
            curr_chess_id.value = res.chess_id
            // // 显示mask，准备开始游戏
            create_tips_show.value = false
            // // 刷新pvp列表
            getPvpList()
            showFloatTips('创建成功', SuccessColor)
            is_wait_player.value = true
          }

          // 悔棋
          const handle_backChess_confirm = async () => {
            const { data: res } = await axios.post(
              `http://${ServerIp}/backChess?p_id=${client_id}&chess_id=${curr_chess_id.value}&other_id=${curr_p_id.value}`
            )
            back_tips_show.value = false
            if (res.error == True) {
              showFloatTips(res.msg, ErrorColor)
              return
            }

            showFloatTips('悔棋成功', SuccessColor)
          }

          // 退出棋局
          const handle_exitChess = async (flag) => {
            const { data: res } = await axios.post(`http://${ServerIp}/exitChess?send_p_id=${client_id}`)
            // console.log(res)
            if (res.error === True) {
              console.log(res.msg)
              showFloatTips(res.msg, ErrorColor)
              return
            }
            is_wait_player.value = false
            board_show.value = false
            clearInterval(gameTimer.value)
          }
          // 加入棋局
          const handle_addChess = (chess_id) => {
            // console.log(chess_id)
            curr_chess_id.value = chess_id
            add_tips_show.value = true
          }
          const handle_addChess_confirm = async () => {
            // console.log(curr_chess_id.value)
            const { data: res } = await axios.post(
              `http://${ServerIp}/pvp?p_id=${client_id}&chess_id=${curr_chess_id.value}`
            )
            // console.log(res)
            if (res.error === True) {
              console.log(res.msg)
              showFloatTips(res.msg, ErrorColor)
              return
            }
            add_tips_show.value = false
          }

          // 获取棋子颜色
          const getChessColor = (index) => {
            if (curr_chess_board.value < 5) return ''
            let color = curr_chess_board.value[parseInt(index / board_col.value)][index % board_col.value]
            let className = ''
            switch (color) {
              case ' ':
                // 没有时不设置颜色
                className = ''
                break
              case '#ffa5d9':
                className = 'chess-back-ffa5d9'
                break
              case '#bccae7':
                className = 'chess-back-bccae7'
                break
            }
            return className
          }

          // 显示tips
          const show_float_tips = ref(false)
          const timer = ref(null)
          const timer2 = ref(null)
          const msg = ref('')
          const bgColor = ref(null)
          const hidden_float_tips = ref(false)
          // 显示浮动tips
          const showFloatTips = (tmsg, tbgColor) => {
            show_float_tips.value = true
            msg.value = tmsg
            bgColor.value = tbgColor
            // console.log('点击了')
            timer.value = setTimeout(() => {
              hidden_float_tips.value = true
              clearTimeout(timer.value)
            }, 500)

            timer2.value = setTimeout(() => {
              // console.log('执行了')
              show_float_tips.value = false
              hidden_float_tips.value = false
              clearTimeout(timer2.value)
            }, 1500)
          }

          const show_game_status_tips = ref(false)
          const game_status_tips = ref('')
          const game_status_tips_color = ref('')
          const hidden_game_status_tips = ref(false)
          const timer3 = ref(null)
          const timer4 = ref(null)
          const showGameStatusTips = (tips, color) => {
            show_game_status_tips.value = true
            game_status_tips.value = tips
            game_status_tips_color.value = color
            timer3.value = setTimeout(() => {
              hidden_game_status_tips.value = true
              clearTimeout(timer3.value)
            }, 200)

            timer4.value = setTimeout(() => {
              show_game_status_tips.value = false
              hidden_game_status_tips.value = false
              clearTimeout(timer4.value)
            }, 1500)
          }

          const change_game_tips = ref({
            msg: '',
            chess_color: ''
          })
          const changeGameTips = (msg, chess_color) => {
            change_game_tips.value.msg = msg
            change_game_tips.value.chess_color = chess_color
          }

          const gameTimer = ref(null)
          let gameTimeNum = 0
          const gameTime = ref('00:00:00')
          // 计时器
          const setTimer = () => {
            gameTimer.value = setInterval(() => {
              // 秒
              let second = gameTimeNum % 60
              second = second < 10 ? '0' + second : second
              let min = parseInt(gameTimeNum / 60) % 60
              min = min < 10 ? '0' + min : min
              let hour = parseInt(gameTimeNum / 60 / 60)
              hour = hour < 10 ? '0' + hour : hour
              gameTime.value = hour + ':' + min + ':' + second
              gameTimeNum++
            }, 1000)
          }

          return {
            board_col,
            board_row,
            chess_size,
            client_id,
            board_show,
            create_tips_show,
            add_tips_show,
            back_tips_show,
            game_info_show,
            pvp_list,
            pvp_list_show,
            model_size,
            curr_chess_board,
            show_float_tips,
            msg,
            bgColor,
            hidden_float_tips,
            show_game_status_tips,
            game_status_tips,
            game_status_tips_color,
            hidden_game_status_tips,
            is_wait_player,
            change_game_tips,
            gameTime,
            GameInfo,
            game_info_index,
            handle_PvP_showBoard,
            handle_setChess,
            handle_createChess,
            handle_addChess,
            handle_addChess_confirm,
            handle_exitChess,
            handle_reconnect,
            handle_backChess_confirm,
            getChessColor
          }
        }
      }).mount('#app')
    </script>
  </body>
</html>
